﻿//-------------------------------------------------------------------
//	Copyright (c) 2012 Fedotov I.V.
//	All rights reserved.
//-------------------------------------------------------------------
/*
	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program. If not, see http://www.gnu.org/licenses/.

	Project SVN page 
*/

#ifndef __MAP_SPTR_H__
#define __MAP_SPTR_H__

#include <stdio.h>

//#define _DEBUG

//~SPtr calls delete
//SPtr<Obj> o1();
//SPtr<char> ch1();
//SPtr<char> ch1(new char);

typedef enum SPtrAllocType{
  SPTR_ALLOC_NORMAL,
	//При таком типе изначально _p=NULL, но при первом вызове функции
	//получения указателя, он предварительно выделяется
	//Список функций, после которых выделится указатель:
	//T *p();
	//
	//
	//Применяется для уменьшения размера мелких сохраняемых объектов
	//(которые имеют SPtr с не выделенным объектом внутри)
	SPTR_ALLOC_ON_CALL
};


template<class T> class SPtr{

private:
	SPtrAllocType allocType;


public:

SPtr(){
	create(new(_NORMAL_BLOCK, __FILE__, __LINE__) T, SPTR_ALLOC_NORMAL);
}

SPtr(const SPtr<T> &val){
	copy(val);
}

SPtr(SPtrAllocType _allocType){
	switch(_allocType){
    case SPTR_ALLOC_NORMAL:
      create(new(_NORMAL_BLOCK, __FILE__, __LINE__) T, SPTR_ALLOC_NORMAL);
			break;
    case SPTR_ALLOC_ON_CALL:
			create(NULL, SPTR_ALLOC_ON_CALL);
			break;
	}
}

SPtr(T *ptr){
	create(ptr, SPTR_ALLOC_NORMAL);
}


~SPtr(){
  deleteP();
}



SPtr<T> & operator=(const SPtr<T> &val){
  if(&val==this){
    return *this;
  }
  deleteP();
  copy(val);
  return *this;
}


//Для случая SPTR_ALLOC_ON_CALL. Чтобы можно было узнать
//есть ли указатель (т.к. обращение к p() создаст его)
int isNULLPtr(){
  if(_p==NULL)return 1;
	return 0;
}

T *p(){
	switch(allocType){
    case SPTR_ALLOC_ON_CALL:
			if(*_p==NULL)_p[0]=new(_NORMAL_BLOCK, __FILE__, __LINE__) T;
			break;
	}
  return *_p;
}

private:

//Указатель на указатель используется для разрешения ситуации при которой объект
//изначально не выделен (случай SPTR_ALLOC_ON_CALL) но уже может быть скопирован
//(например SPtr<> приравнен к дргому SPtr<>) и далее создан в скопированном объекте.
//В итоге создание объекта в копии, повлечет за собой "появление" объекта и в этом
//объекте (объект создастся 1 раз, но во всех скопированных объектах сохранен
//указатель на указатель на него)
T **_p;
int *retainCountPtr;

void create(T *ptr, SPtrAllocType _allocType){
	allocType=_allocType;
	_p=new(_NORMAL_BLOCK, __FILE__, __LINE__) T *[1];
	_p[0]=ptr;
	retainCountPtr=new(_NORMAL_BLOCK, __FILE__, __LINE__) int;
	*retainCountPtr=1;
}

void copy(const SPtr<T> &val){
	_p=val._p;
  retainCountPtr=val.retainCountPtr;
	(*retainCountPtr)++;
	allocType=val.allocType;
}

void deleteP(){
	if(retainCountPtr==NULL){
		return;
	}
  (*retainCountPtr)--;
  if(*retainCountPtr<=0){
		if(_p!=NULL){
		  if(_p[0]!=NULL){
        delete _p[0];
        _p[0]=NULL;
      }
			delete [] _p;
      _p=NULL;
    }
    if(retainCountPtr!=NULL){
      delete retainCountPtr;
      retainCountPtr=NULL;
	  }
  }
}

};

#endif